package com.browseengine.bobo.facets.filter;

import java.io.IOException;

import org.apache.lucene.search.DocIdSetIterator;
import org.apache.lucene.util.OpenBitSet;

import com.browseengine.bobo.api.BoboSegmentReader;
import com.browseengine.bobo.docidset.EmptyDocIdSet;
import com.browseengine.bobo.docidset.RandomAccessDocIdSet;
import com.browseengine.bobo.facets.data.FacetDataCache;
import com.browseengine.bobo.facets.data.MultiValueFacetDataCache;
import com.browseengine.bobo.facets.filter.AdaptiveFacetFilter.FacetDataCacheBuilder;
import com.browseengine.bobo.facets.range.BitSetBuilder;

public class BitSetFilter extends RandomAccessFilter {
  protected final FacetDataCacheBuilder facetDataCacheBuilder;
  protected final BitSetBuilder bitSetBuilder;
  private volatile OpenBitSet bitSet;
  private volatile FacetDataCache<?> lastCache;

  public BitSetFilter(BitSetBuilder bitSetBuilder, FacetDataCacheBuilder facetDataCacheBuilder) {
    this.bitSetBuilder = bitSetBuilder;
    this.facetDataCacheBuilder = facetDataCacheBuilder;
  }

  public OpenBitSet getBitSet(FacetDataCache<?> dataCache) {
    if (lastCache == dataCache) {
      return bitSet;
    }
    bitSet = bitSetBuilder.bitSet(dataCache);
    lastCache = dataCache;
    return bitSet;
  }

  @Override
  public RandomAccessDocIdSet getRandomAccessDocIdSet(final BoboSegmentReader reader)
      throws IOException {
    final FacetDataCache<?> dataCache = facetDataCacheBuilder.build(reader);
    final OpenBitSet openBitSet = getBitSet(dataCache);
    long count = openBitSet.cardinality();
    if (count == 0) {
      return EmptyDocIdSet.getInstance();
    } else {
      final boolean multi = dataCache instanceof MultiValueFacetDataCache;
      final MultiValueFacetDataCache<?> multiCache = multi ? (MultiValueFacetDataCache<?>) dataCache
          : null;

      return new RandomAccessDocIdSet() {
        @Override
        public DocIdSetIterator iterator() {

          if (multi) {
            return new MultiValueORFacetFilter.MultiValueOrFacetDocIdSetIterator(multiCache,
                openBitSet);
          } else {
            return new FacetOrFilter.FacetOrDocIdSetIterator(dataCache, openBitSet);

          }
        }

        @Override
        public boolean get(int docId) {
          if (multi) {
            return multiCache._nestedArray.contains(docId, openBitSet);
          } else {
            return openBitSet.fastGet(dataCache.orderArray.get(docId));
          }
        }
      };
    }
  }

  @Override
  public double getFacetSelectivity(BoboSegmentReader reader) {
    FacetDataCache<?> dataCache = facetDataCacheBuilder.build(reader);
    final OpenBitSet openBitSet = getBitSet(dataCache);
    int[] frequencies = dataCache.freqs;
    double selectivity = 0;
    int accumFreq = 0;
    int index = openBitSet.nextSetBit(0);
    while (index >= 0) {
      accumFreq += frequencies[index];
      index = openBitSet.nextSetBit(index + 1);
    }
    int total = reader.maxDoc();
    selectivity = (double) accumFreq / (double) total;
    if (selectivity > 0.999) {
      selectivity = 1.0;
    }
    return selectivity;
  }
}
